using System;
using System.Collections.Generic;
using System.Text;

using System.Drawing;
using System.Security.Cryptography;

namespace PictureMimic
{
	public abstract class CandidateMutationModel
	{
		public abstract bool Mutate(CandidateGene gene);

		public virtual bool MutatePoints(CandidateGene gene)
		{
			byte[] seed;
			RNGCryptoServiceProvider seeder;
			Random randomiser;

			seed = new byte[32];
			seeder = new RNGCryptoServiceProvider();
			seeder.GetBytes(seed);

			randomiser = new Random((int)BitConverter.ToInt32(seed, 0));

			if (randomiser.Next(0, 2) < 1)
			{
				// Let's modify the number of points...

				int pointCount = randomiser.Next(3, EvolutionaryConstraints.MaxPoints + 1);

				if (pointCount == gene.Points.Length)
				{
					return false;
				}

				Point[] newPoints = new Point[pointCount];

				for (int i = 0; i < pointCount; i++)
				{
					if (i < gene.Points.Length)
					{
						newPoints[i] = gene.Points[i];
					}
					else
					{
						int x = randomiser.Next(0, EvolutionaryConstraints.EnvironmentWidth + 1);
						int y = randomiser.Next(0, EvolutionaryConstraints.EnvironmentHeight + 1);

						newPoints[i] = new Point(x, y);
					}
				}

				gene.Points = newPoints;
			}
			else
			{
				// Let's modify an existing point...
				int whichPoint = randomiser.Next(0, gene.Points.Length);

				if (randomiser.Next(0, 2) < 1)
				{
					// Let's modify the x-axis...
					int x = randomiser.Next(0, EvolutionaryConstraints.EnvironmentWidth + 1);
					int y = gene.Points[whichPoint].Y;

					gene.Points[whichPoint] = new Point(x, y);
				}
				else
				{
					// Let's modify the y-axis...
					int x = gene.Points[whichPoint].X;
					int y = randomiser.Next(0, EvolutionaryConstraints.EnvironmentHeight + 1);

					gene.Points[whichPoint] = new Point(x, y);
				}
			}

			return true;
		}

		public virtual bool MutateColour(CandidateGene gene)
		{
			int a = gene.Colour.A;
			int r = gene.Colour.R;
			int g = gene.Colour.G;
			int b = gene.Colour.B;

			byte[] seed;
			RNGCryptoServiceProvider seeder;
			Random randomiser;

			seed = new byte[32];
			seeder = new RNGCryptoServiceProvider();
			seeder.GetBytes(seed);

			randomiser = new Random((int)BitConverter.ToInt32(seed, 0));

			int whichAttribute = randomiser.Next(1, 5);

			switch (whichAttribute)
			{
				case 1:
					a = randomiser.Next(0, 256);
					break;
				case 2:
					r = randomiser.Next(0, 256);
					break;
				case 3:
					g = randomiser.Next(0, 256);
					break;
				case 4:
					b = randomiser.Next(0, 256);
					break;
				default:
					return false;
			}

			gene.Colour = Color.FromArgb(a, r, g, b);

			return true;
		}
	}

	/*** No Mutation... Pretty straight forward. ***/
	public class NoMutation : CandidateMutationModel
	{
		public override bool Mutate(CandidateGene gene)
		{
			return false;
		}
	}

	/*** Nearly every gene will mutate... ***/
	public class RampantMutation : CandidateMutationModel
	{
		public override bool Mutate(CandidateGene gene)
		{
			bool mutated = false;

			byte[] seed;
			RNGCryptoServiceProvider seeder;
			Random randomiser;

			seed = new byte[32];
			seeder = new RNGCryptoServiceProvider();
			seeder.GetBytes(seed);

			randomiser = new Random((int)BitConverter.ToInt32(seed, 0));

			//if (randomiser.Next(0, 2) < 1)
			//{
			mutated = mutated || base.MutateColour(gene);
			//}

			//if (randomiser.Next(0, 2) < 1)
			//{
			mutated = mutated || base.MutatePoints(gene);
			//}

			return mutated;
		}
	}

	/*** Coin Flip (50/50) Chance of Mutation. ***/
	public class CoinFlipMutation : CandidateMutationModel
	{
		public override bool Mutate(CandidateGene gene)
		{
			bool mutated = false;

			byte[] seed;
			RNGCryptoServiceProvider seeder;
			Random randomiser;

			seed = new byte[32];
			seeder = new RNGCryptoServiceProvider();
			seeder.GetBytes(seed);

			randomiser = new Random((int)BitConverter.ToInt32(seed, 0));

			if (randomiser.Next(0, 2) < 1)
			{
				if (randomiser.Next(0, 2) < 1)
				{
					mutated = base.MutateColour(gene);
				}
				else
				{
					mutated = base.MutatePoints(gene);
				}
			}

			return mutated;
		}
	}

	/*** A chance for one or more mutations per crossover. ***/
	public class CommonMutation : CandidateMutationModel
	{
		public override bool Mutate(CandidateGene gene)
		{
			bool mutated = false;

			byte[] seed;
			RNGCryptoServiceProvider seeder;
			Random randomiser;

			seed = new byte[32];
			seeder = new RNGCryptoServiceProvider();
			seeder.GetBytes(seed);

			randomiser = new Random((int)BitConverter.ToInt32(seed, 0));

			if (randomiser.Next(1, 51) == 1)
			{
				if (randomiser.Next(0, 2) < 1)
				{
					mutated = base.MutateColour(gene);
				}
				else
				{
					mutated = base.MutatePoints(gene);
				}
			}

			return mutated;
		}
	}

	/*** Mutate only when Inbred ***/
	public class InbredMutation : CandidateMutationModel
	{
		public override bool Mutate(CandidateGene gene)
		{
			bool mutated = false;

			if (gene.Inbred)
			{
				byte[] seed;
				RNGCryptoServiceProvider seeder;
				Random randomiser;

				seed = new byte[32];
				seeder = new RNGCryptoServiceProvider();
				seeder.GetBytes(seed);

				randomiser = new Random((int)BitConverter.ToInt32(seed, 0));

				if (randomiser.Next(0, 2) < 1)
				{
					mutated = base.MutateColour(gene);
				}
				else
				{
					mutated = base.MutatePoints(gene);
				}
			}

			return mutated;
		}
	}
}
